from passlib.context import CryptContext
from datetime import datetime, timedelta, timezone
from typing import Optional
import json

from fastapi import APIRouter, HTTPException, Response, Depends

from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm, SecurityScopes

import jwt
# from redis.asyncio import Redis

from account.models import AccountORM, QAs
from account.schemas import AccountSubmitted, AccountPublic, AccountDetail, ApiResponse, SetRoleSubmitted
from database.redis import get_redis_client
from config import config

router = APIRouter()

pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
# redis = get_redis_client()


def get_password_hash(password: str):
    return pwd_context.hash(password)


async def is_user_exist(username: str):
    return await AccountORM.get_or_none(username=username)


async def verfify_password(password: str, hashed_password: str):
    return pwd_context.verify(password, hashed_password)


def create_user_token(data: dict, expires_delta: Optional[timedelta] = None):
    to_encode = data.copy()
    expire = datetime.now(timezone.utc) + \
        (expires_delta or timedelta(minutes=config.JWT_ACCESS_TOKEN_EXPIRE_MINUTES))
    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(
        to_encode, config.JWT_SECRET_KEY, algorithm=config.JWT_ALGORITHM)
    return encoded_jwt


def check_user(token: str = Depends(OAuth2PasswordBearer(tokenUrl="login", scheme_name="Bearer"))):
    user = AccountDetail()
    try:
        payload = jwt.decode(
            token, config.JWT_SECRET_KEY, algorithms=[config.JWT_ALGORITHM])
        print("payload: ", payload)
        user.id = payload.get("id")
        user.role = payload.get("role")
        user.username = payload.get("username")
    except jwt.ExpiredSignatureError:
        user.error = "error: token expired"
    except jwt.InvalidTokenError:
        user.error = "error: invalid token"
    except jwt.PyJWTError as e:
        user.error = f"JWT error: {e}"
    return user


# @router.post("/signup", response_model=AccountPublic)
@router.post("/signup")
async def signup(user: AccountSubmitted):
    username, pwd = user.username, user.password
    user = await is_user_exist(user.username)
    if user:
        raise HTTPException(status_code=409, detail='user already existed')
    hashed_password = get_password_hash(pwd)
    try:
        newUser = await AccountORM.create(username=username, hashed_password=hashed_password)
        await login(AccountSubmitted(username=username, password=pwd))
        return ApiResponse(status_code=201, message=f'Create user {newUser.username} succeed')
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))


@router.post("/login")
async def login(user: AccountSubmitted):
    pwd = user.password
    user = await is_user_exist(user.username)
    if not user:
        raise HTTPException(
            status_code=404, detail='username does not exist')
    if not await verfify_password(pwd, user.hashed_password):
        raise HTTPException(
            status_code=401, detail='password does not match')
    userInfo = {"id": user.id,
                "username": user.username, "role": user.role}
    token = create_user_token(userInfo)
    data = {"username": user.username, "token": token}
    message = f"user {user.username} logged in"
    return ApiResponse(status_code=200, message=message, data=data)


# @router.get("/users/{user_id}", response_model=AccountPublic)
# async def read_user(user_id: int):
#     # 尝试从 Redis 缓存获取
#     cached = await redis.get(f"user:{user_id}")
#     if cached:
#         return AccountPublic(**json.loads(cached))

#     # 数据库查询
#     user = await AccountORM.get_or_none(id=user_id)
#     if not user:
#         raise HTTPException(404, "User not found")

#     # 写入缓存（设置TTL 60秒）
#     await redis.set(f"user:{user_id}", json.dumps(user.to_dict_complete()), ex=config.REDIS_TTL)
#     return user


@router.put("/setrole")
async def set_role(srs: SetRoleSubmitted, user: AccountDetail = Depends(check_user)):
    if user.error:
        raise HTTPException(status_code=401, detail=user.error)
    if user.role != "admin":
        print(user)
        user.role = srs.roleToSet
        await AccountORM.filter(id=user.id).update(role=user.role)
        userInfo = {"id": user.id,
                    "username": user.username, "role": user.role}
        newtoken = create_user_token(userInfo)
        data = {"username": user.username, "token": newtoken}
        return ApiResponse(status_code=200, message=f'user {user.username} role set to {user.role}', data=data)
